package org.dxw.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.page.PageMethod;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.util.CellRangeAddress;
import org.dxw.DO.UserDO;
import org.dxw.common.excel.ExportUtil;
import org.dxw.common.excel.MergeParam;
import org.dxw.common.excel.MySheetWriteHandler;
import org.dxw.mapper.UserMapper;
import org.dxw.req.UserPageReq;
import org.dxw.resp.PageResponse;
import org.dxw.service.UserService;
import org.dxw.vo.UserExport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    private ExecutorService executorService = Executors.newFixedThreadPool(5);


    @Override
    public PageResponse<UserDO> page(UserPageReq userPageReq) {
        PageMethod.startPage(userPageReq.getPageNo(), userPageReq.getPageSize(), "createTime desc");
        List<UserDO> list = userMapper.list(userPageReq);
        return PageResponse.success(list, new PageInfo<>(list).getTotal());
    }


    @Override
    public void export(UserPageReq userPageReq, HttpServletResponse response) throws IOException {
        List<String> excludeColumnFieldNames = null;
        //1 要合并单元格(多行变一行) 的列索引，eg：同一个角色合并
        List<Integer> userColumnIndexs = new ArrayList<>();
        MergeParam<UserExport, Date> userMerge = new MergeParam<>(UserExport::getCreateTime,
                userColumnIndexs);

        List<Integer> roleColumnIndexs = new ArrayList<>();
        MergeParam<UserExport, String> roleMerge = new MergeParam<>(UserExport::getRoleKey,
                roleColumnIndexs);

        if(userPageReq.isDebug()){
            excludeColumnFieldNames = Arrays.asList("age");
            userColumnIndexs.add(5);
            roleColumnIndexs.add(6);
            roleColumnIndexs.add(7);
        }else{
            userColumnIndexs.add(4);
            roleColumnIndexs.add(5);
            roleColumnIndexs.add(6);
        }
        simpleExport(userPageReq, response, excludeColumnFieldNames, Arrays.asList(userMerge, roleMerge));
    }


    private void simpleExport(UserPageReq userPageReq, HttpServletResponse response, List<String> excludeColumnFieldNames, List<MergeParam> mergeParams) throws IOException {
        int sheetNo = 0;
        String sheetName = "明细";
        ExcelWriter excelWriter = EasyExcel.
                write(response.getOutputStream()).autoCloseStream(true).build();
        PageHelper.startPage(userPageReq.getPageNo(), userPageReq.getPageSize() , false);
        List<UserDO> userDOS = userMapper.list(userPageReq);
        List<UserExport> userExports = BeanUtil.copyToList(userDOS, UserExport.class);
        UserExport nullUser = new UserExport();
        for (UserExport userExport : userExports) {
            userExport.setCreateTime(DateUtil.beginOfDay(userExport.getCreateTime()));
        }
        //help gc
        userDOS = null;
        List<CellRangeAddress> cellRangeAddresses = ExportUtil.generatorCellRange(2, userExports, mergeParams);
        MySheetWriteHandler mySheetWriteHandler = new MySheetWriteHandler();
        mySheetWriteHandler.getSheetCellRange().put(sheetName, cellRangeAddresses);

        WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, sheetName)
                .head(UserExport.class)
                .excludeColumnFieldNames(excludeColumnFieldNames)
                .registerWriteHandler(mySheetWriteHandler)
                .build();
        excelWriter.write(userExports, writeSheet);
        excelWriter.finish();
    }

    /**
     * @warn 这个方法不能合并单元格，因为数据是分批查询，导出的，不同批次的数据得不到统一的排序，计算应该合并
     * 的单元格
     *  百万级数据分批导出 写入到excel
     *  对于业务复杂，任务难以分解的，难搞。
     *  因为有些导出，要对所有的数据进行排序， 分组，统计， 所以需要 把所有数据都load出来，在进行运算导出，
     *  这会对占用比较大的内存。
     *  如果任务可以拆解的话， 是可以用多线程进行提升性能的。比如
     *  任务拆解成了
     *  任务1，2，3，4，5
     *  1 写出到 数据1 到 10000
     *  2  10001 到 20000
     *  ...
     * @param userPageReq
     * @param response
     * @param excludeColumnFieldNames
     * @throws IOException
     */
    private void optiBigDataExport(UserPageReq userPageReq,HttpServletResponse response,  List<String> excludeColumnFieldNames) throws IOException {
        ExcelWriter excelWriter = EasyExcel.
                write(response.getOutputStream()).autoCloseStream(true).build();
        int sheetWirtedRows = 0;
        final int sheetMaxRows = 1000000;
        int sheetNo = 0;
        final int batchSize = 5000;
        long nextMinId = 0;
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, "明细"+sheetNo).head(UserExport.class)
                .excludeColumnFieldNames(excludeColumnFieldNames).build();
        for (;;) {
            List<UserDO> userDOS = userMapper.selectBatch(userPageReq, nextMinId, batchSize);
            List<UserExport> userExports = BeanUtil.copyToList(userDOS, UserExport.class);
            if (userExports.isEmpty()) {
                excelWriter.finish();
                break;
            }
            Long id = userExports.get(userExports.size()-1).getUserId();
            nextMinId = id;
            sheetWirtedRows += userExports.size();
            if(sheetWirtedRows + 2 <= sheetMaxRows){//加上一个头所占的行
                excelWriter.write(userExports, writeSheet);
            }else{
                sheetNo++;
                writeSheet = EasyExcel.writerSheet(sheetNo, "明细"+sheetNo).head(UserExport.class)
                        .excludeColumnFieldNames(excludeColumnFieldNames).build();
                excelWriter.write(userExports, writeSheet);
                sheetWirtedRows = userExports.size();
            }
        }

    }


    @Override
    public void test(UserPageReq userPageReq, HttpServletResponse response) {
        userMapper.test(userPageReq);
    }




}
